package sk.ownage.p1.featurestore;

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.graphics.Color;
import android.os.Environment;
import android.util.Log;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

public class TrackMapOverlay {
	final String TAG = "TrackMapOverlay";
	final LatLngBounds boundingBox = new LatLngBounds(
			new LatLng(-85.0, -180.0), new LatLng(85.0, 179.999999));
	static final int DIRECTORY_MAX = TrackStore.DIRECTORY_MAX;
	final String rootPath = Environment.getExternalStorageDirectory()
			+ "/geometryStore" + "/tracks";
	private String trackRootPath="";
	int threshold = TrackQuadTreeRoot.actualThreshold;
	int actualZoomLevel=0;
	Map<String, List<Polyline>> actualTrackPolylinesList = new HashMap<String, List<Polyline>>();
	Map<String, Long> pathsLastModificationTime = new HashMap<String, Long>();
	Set<String> allNewTrackPolylinesListsPaths = new HashSet<String>();
	List<Polyline> actualTrackBoundingPolylines = new ArrayList<Polyline>();
	String actualPath="";
	
	public TrackMapOverlay(int trackId){
		this(trackId,"");
	} 
	
	public TrackMapOverlay(int trackId, String trackRootPath){
		this.trackRootPath=trackRootPath;
		int folderId = trackId / DIRECTORY_MAX;
		int fileId = trackId % DIRECTORY_MAX;
		String folderPath = (((folderId / 100) > 0) ? "" : "0")
				+ (((folderId / 10) > 0) ? "" : "0") + folderId;
		String filePath = (((fileId / 100) > 0) ? "" : "0")
				+ (((fileId / 10) > 0) ? "" : "0") + fileId;
		actualPath = ((!this.trackRootPath.equals(""))?this.trackRootPath:rootPath) + "/" + folderPath + "/" + filePath + "/";
	} 
	
	public void cleanMap() {
		/*for (Polyline polyline : actualTrackBoundingPolylines) {
			polyline.remove();
		}*/
		actualTrackBoundingPolylines = new ArrayList<Polyline>();
		for (List<Polyline> polylineList : actualTrackPolylinesList.values()) {
			if (polylineList != null)
				for (Polyline polyline : polylineList) {
					polyline.remove();
				}
		}
		actualTrackPolylinesList = new HashMap<String, List<Polyline>>();
	}

	public void drawTrack(GoogleMap gmap, int color) {
		File geometryFile=new File(actualPath+"/"+actualZoomLevel+"/size.bin");
		if(!geometryFile.exists()){
			return;
		}else{
			try {
				FileInputStream fis = new FileInputStream(geometryFile);
				ObjectInputStream is = new ObjectInputStream(
						new BufferedInputStream(fis));
				is.readInt();
				is.readBoolean();
				is.readBoolean();
				threshold = is.readInt();
				is.close();
			} catch (Exception e) {
				Log.d(TAG, e.toString());
			}
		}
		int newZoomLevel=((int)gmap.getCameraPosition().zoom-4)/3;
		newZoomLevel=(newZoomLevel>4)?4:newZoomLevel;
		if(actualZoomLevel!=newZoomLevel){
			cleanMap();
			actualZoomLevel=newZoomLevel;
		}
		
		for (Polyline polyline : actualTrackBoundingPolylines) {
			polyline.remove();
		}
		actualTrackBoundingPolylines = new ArrayList<Polyline>();
		
		allNewTrackPolylinesListsPaths = new HashSet<String>();
		Map<String, List<Polyline>> result = getPolylines(gmap.getProjection()
				.getVisibleRegion().latLngBounds, "", boundingBox.southwest,
				boundingBox.northeast, gmap, color);
		/*
		 * for (String path : new_paths2) { if(!result.containsKey(path))
		 * result.put(path,old_List.get(path)); }
		 */
		List<String> to_remove = new ArrayList<String>();
		for (String path : actualTrackPolylinesList.keySet()) {
			if (!allNewTrackPolylinesListsPaths.contains(path)) {
				to_remove.add(path);
			}
		}
		for (String path : to_remove) {
			pathsLastModificationTime.remove(path);
			for (Polyline polyline : actualTrackPolylinesList.get(path))
				polyline.remove();
			actualTrackPolylinesList.remove(path);
		}
		actualTrackPolylinesList.putAll(result);
	}

	public Map<String, List<Polyline>> getPolylines(LatLngBounds boundingBox1,
			String _path, LatLng _SW, LatLng _NE, GoogleMap gmap, int color) {

		// Log.d(TAG,"vstup do getMarkers");
		double averageLat = (_SW.latitude + _NE.latitude) / 2;
		double averageLon = (_SW.longitude + _NE.longitude) / 2;
		LatLng tempCenter = new LatLng(averageLat, averageLon);

		if (tempCenter.longitude > 90 || true) {
			
			/*  PolylineOptions temp =new PolylineOptions(); temp.add(_SW);
			  temp.add(new LatLng(_SW.latitude, _NE.longitude)); temp.add(_NE);
			  temp.add(new LatLng(_NE.latitude, _SW.longitude)); temp.add(_SW);
			  temp.color(Color.rgb((int)Math.round(Math.random()*255),(int)
			  Math.round(Math.random()*255),
			 (int)Math.round(Math.random()*255))); //if(GeometryTest.what==0){
			  actualTrackBoundingPolylines.add(gmap.addPolyline(temp));*/
			 
			/*
			 * for (LatLng loc: temp.getPoints()) {
			 * Log.d(TAG,"////////////////");
			 * Log.d(TAG,"loca lon: "+loc.latitude+"loca lon:"+loc.longitude); }
			 * Log.d(TAG,"NW lon: "+NW.latitude+"NE lon:"+boundingBox.northeast.
			 * longitude
			 * +"SE lon:"+SE.longitude+"SW lon:"+boundingBox.southwest.longitude
			 * ); } Log.d(TAG,GeometryTest.what+"");
			 */
			// GeometryTest.what++;
		}

		Map<String, List<Polyline>> result = new HashMap<String, List<Polyline>>();

		LatLngBounds bbox2 = new LatLngBounds(_SW, _NE);
		boolean condition = (boundingBox1.contains(new LatLng(_NE.latitude,
				_SW.longitude))
				|| boundingBox1
						.contains(new LatLng(_SW.latitude, _NE.longitude))
				|| boundingBox1.contains(_NE)
				|| boundingBox1.contains(_SW)
				|| boundingBox1.contains(tempCenter)
				|| bbox2.contains(boundingBox1.northeast)
				|| bbox2.contains(boundingBox1.southwest)
				|| bbox2.contains(new LatLng(boundingBox1.southwest.latitude,
						boundingBox1.northeast.longitude)) || bbox2
				.contains(new LatLng(boundingBox1.northeast.latitude,
						boundingBox1.southwest.longitude)));
		if (!condition)
			return result;
		FileInputStream fis;
		ObjectInputStream is;
		if (_path.equals(""))
			_path = actualPath+"/"+actualZoomLevel;
		File sizePath = new File(_path + "/" + "size.bin");
		int flag = 0;
		//boolean divided = false;
		if (sizePath.exists())
			try {
				fis = new FileInputStream(sizePath);
				is = new ObjectInputStream(new BufferedInputStream(fis));
				flag = is.readInt();
				//divided = is.readBoolean();
				is.close();
				// Log.d(TAG,"nacitanee "+sizePath);
			} catch (Exception e) {
				Log.d(TAG, e.toString());
			}
		// Log.d(TAG,"velkost je "+flag);
		File inputStream = new File(_path + "/" + "list.bin");
		if (flag>=threshold) {
			File tempFile;
			tempFile = new File(_path + "/" + 0 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getPolylines(boundingBox1, _path + "/" + 0,
						tempCenter, _NE, gmap, color));
			tempFile = new File(_path + "/" + 1 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getPolylines(boundingBox1, _path + "/" + 1,
						new LatLng(tempCenter.latitude, _SW.longitude),
						new LatLng(_NE.latitude, tempCenter.longitude), gmap, color));
			tempFile = new File(_path + "/" + 2 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getPolylines(boundingBox1, _path + "/" + 2,
						new LatLng(_SW.latitude, tempCenter.longitude),
						new LatLng(tempCenter.latitude, _NE.longitude), gmap, color));
			tempFile = new File(_path + "/" + 3 + "/size.bin");
			if (tempFile.exists())
				result.putAll(getPolylines(boundingBox1, _path + "/" + 3, _SW,
						tempCenter, gmap, color));

		} else {
			if (actualTrackPolylinesList.containsKey(_path)
					&& (pathsLastModificationTime.get(_path) != null && inputStream
							.lastModified() <= pathsLastModificationTime.get(_path)))
				// new_paths.add(_path);
				allNewTrackPolylinesListsPaths.add(_path);
			else
				try {
					pathsLastModificationTime.put(_path, inputStream.lastModified());
					fis = new FileInputStream(inputStream);
					is = new ObjectInputStream(new BufferedInputStream(fis));
					PolylineOptions tempPolylineOptions = new PolylineOptions();
					// Log.d(TAG,"size"+inputStream.length());
					while (flag > 0) {
						int copy = is.readByte();
						if (copy == 1) {
							if (tempPolylineOptions != null) {
								if (!result.containsKey(_path))
									result.put(_path, new ArrayList<Polyline>());

								// new_paths.add(_path);
								allNewTrackPolylinesListsPaths.add(_path);
								result.get(_path)
										.add(gmap
												.addPolyline(tempPolylineOptions.color((color!=0)?color:Color.rgb(
														(int) Math
																.round(Math
																		.random() * 255),
														(int) Math.round(Math
																.random() * 255),
														(int) Math.round(Math
																.random() * 255)))));
							}
							tempPolylineOptions = new PolylineOptions();
						}
						tempPolylineOptions.add(new LatLng(is.readDouble(), is
								.readDouble()));
						/*
						 * else { is.readDouble(); is.readDouble(); }
						 */
						// is.readDouble(); is.readDouble();is.readUTF();
						// Log.d(TAG,"lat: "+is.readDouble()+" long "+is.readDouble()+" title "+is.readUTF());
						// Log.d(TAG,"pridany do listu "+flag+"size: "+result.size());
						flag--;
					}
					if (!result.containsKey(_path))
						result.put(_path, new ArrayList<Polyline>());
					// new_paths.add(_path);
					allNewTrackPolylinesListsPaths.add(_path);
					result.get(_path)
							.add(gmap.addPolyline(tempPolylineOptions.color((color!=0)?color:Color.rgb(
									(int) Math.round(Math.random() * 255),
									(int) Math.round(Math.random() * 255),
									(int) Math.round(Math.random() * 255)))));
				} catch (EOFException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				} catch (FileNotFoundException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				} catch (StreamCorruptedException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				} catch (IOException e) {
					Log.e(TAG, e.toString());
					e.printStackTrace();
				}
		}
		return result;
	}

}
